package net.dntuan.training.spring.config;

import javax.servlet.Filter;

import net.dntuan.training.spring.security.DBAuthenticationProvider;
import net.dntuan.training.spring.security.MyWebAuthenticationDetailsSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter;
import org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.filter.RequestContextFilter;

@Configuration
@EnableWebMvcSecurity
@ImportResource("classpath:oauth2/oauth2-config.xml")
@ComponentScan("net.dntuan.training.spring.security")
public class MultiHttpSecurityConfig {

    @Configuration
    @Order(1)
    public static class AuthorizeServer extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(getClientDetailsUserDetailsService());
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                .requestMatchers()
                    .antMatchers("/oauth/token")
                    .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .userDetailsService(getClientDetailsUserDetailsService())
                .authorizeRequests()
                    .antMatchers("/oauth/token").authenticated()
                    .and()
                .anonymous().disable()
                .httpBasic()
                    .authenticationEntryPoint(getClientAuthenticationEntryPoint())
                    .and()
                .addFilterBefore(new RequestContextFilter(), BasicAuthenticationFilter.class)
                .addFilterAfter(getClientCredentialsTokenEndpointFilter(), BasicAuthenticationFilter.class)
                .exceptionHandling()
                    .accessDeniedHandler(new OAuth2AccessDeniedHandler());
            // @formatter:on
        }

        @Autowired
        private ClientDetailsService clientDetails;

        @Bean(name = "clientAuthenticationManager")
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }

        @Bean(name = "clientDetailsUserService")
        public UserDetailsService getClientDetailsUserDetailsService() {
            return new ClientDetailsUserDetailsService(clientDetails);
        }

        private Filter getClientCredentialsTokenEndpointFilter() throws Exception {
            AbstractAuthenticationProcessingFilter filter = new ClientCredentialsTokenEndpointFilter();
            filter.setAuthenticationManager(authenticationManagerBean());
            return filter;
        }

        private AuthenticationEntryPoint getClientAuthenticationEntryPoint() {
            OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
            entryPoint.setTypeName("Basic");
            entryPoint.setRealmName("AuthorizationServer");
            return entryPoint;
        }
    }

    @Configuration
    @Order(2)
    public static class SecurityConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        private DBAuthenticationProvider dbAuthenticationProvider;

        @Autowired
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(dbAuthenticationProvider);
        }

//        @Bean(name = "org.springframework.security.authenticationManager")
//        public AuthenticationManager authenticationManagerBean() throws Exception {
//            return super.authenticationManagerBean();
//        }

        @Autowired
        private MyWebAuthenticationDetailsSource myWebAuthenticationDetailsSource;

        @Override
        public void configure(WebSecurity web) throws Exception {
            // @formatter:off
           web.ignoring()
//               .antMatchers("/oauth/cache_approvals")
//               .antMatchers("/oauth/uncache_approvals")
               .antMatchers("/public/**");
           // @formatter:on
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .formLogin()
                    .loginPage("/login")
                    .loginProcessingUrl("/login-processing-url")
                    .usernameParameter("j_username")
                    .passwordParameter("j_password")
                    .authenticationDetailsSource(myWebAuthenticationDetailsSource)
                    .defaultSuccessUrl("/welcome")
                    .permitAll()
                    .and()
                .logout()
                    .invalidateHttpSession(true)
                    .deleteCookies()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout", RequestMethod.GET.name()))
                    .logoutSuccessUrl("/login")
                    .permitAll();
            // @formatter:on
        }

    }
}
